use objc_encode::{Encode, Encoding};

use std::os::raw::c_void;

pub type CGContextRef = *mut c_void;

#[cfg(target_pointer_width = "64")]
pub type CGFloat = f64;

extern "C" {
  // 获取当前图形上下文
  pub fn UIGraphicsGetCurrentContext() -> CGContextRef;

  // 释放一个先前由Core Graphics创建的图形上下文
  pub fn CGContextRelease(c: CGContextRef);

  // 在上下文中添加一个圆弧
  pub fn CGContextAddArc(
    c: CGContextRef,
    x: CGFloat,
    y: CGFloat,
    radius: CGFloat,
    startAngle: CGFloat,
    endAngle: CGFloat,
    clockwise: i32,
  );

  // 移动当前点到指定位置，用于后续的绘图操作
  pub fn CGContextMoveToPoint(c: CGContextRef, x: CGFloat, y: CGFloat);

  // 添加一条线段到指定点
  pub fn CGContextAddLineToPoint(c: CGContextRef, x: CGFloat, y: CGFloat);

  // 填充路径
  pub fn CGContextFillPath(c: CGContextRef);

  // 在路径中添加一个二次贝塞尔曲线点
  pub fn CGContextAddQuadCurveToPoint(c: CGContextRef, cpx: CGFloat, cpy: CGFloat, x: CGFloat, y: CGFloat);

  // 描绘路径
  pub fn CGContextStrokePath(c: CGContextRef);

  pub fn CGContextRetain(c: CGContextRef) -> CGContextRef;

  pub fn CGContextAddEllipseInRect(c: CGContextRef, rect: CGRect);

  pub fn CGContextClip(c: CGContextRef);
}

#[repr(C)]
#[derive(Clone, Copy, Debug, Default)]
pub struct CGPoint {
  pub x: CGFloat,
  pub y: CGFloat,
}

impl CGPoint {
  pub fn new(x: CGFloat, y: CGFloat) -> CGPoint {
    CGPoint { x, y }
  }
}

unsafe impl Encode for CGPoint {
  const ENCODING: Encoding<'static> = Encoding::Struct("CGPoint", &[CGFloat::ENCODING, CGFloat::ENCODING]);
}

unsafe impl objc::Encode for CGPoint {
  fn encode() -> objc::Encoding {
    unsafe { objc::Encoding::from_str(&CGPoint::ENCODING.to_string()) }
  }
}

#[repr(C)]
#[derive(Clone, Copy, Debug, Default)]
pub struct CGSize {
  pub width: CGFloat,
  pub height: CGFloat,
}

impl CGSize {
  fn new(width: CGFloat, height: CGFloat) -> CGSize {
    CGSize { width, height }
  }
}

unsafe impl Encode for CGSize {
  const ENCODING: Encoding<'static> = Encoding::Struct("CGSize", &[CGFloat::ENCODING, CGFloat::ENCODING]);
}

unsafe impl objc::Encode for CGSize {
  fn encode() -> objc::Encoding {
    unsafe { objc::Encoding::from_str(&CGSize::ENCODING.to_string()) }
  }
}

#[repr(C)]
#[derive(Clone, Copy, Debug, Default)]
pub struct CGRect {
  pub origin: CGPoint,
  pub size: CGSize,
}

impl CGRect {
  pub fn new(origin: CGPoint, size: CGSize) -> CGRect {
    CGRect { origin, size }
  }

  pub fn make(x: CGFloat, y: CGFloat, width: CGFloat, height: CGFloat) -> CGRect {
    CGRect {
      origin: CGPoint::new(x, y),
      size: CGSize::new(width, height),
    }
  }
}

unsafe impl objc::Encode for CGRect {
  fn encode() -> objc::Encoding {
    let str = objc_encode::Encoding::Struct("CGRect", &[CGPoint::ENCODING, CGSize::ENCODING]).to_string();
    unsafe { objc::Encoding::from_str(&str) }
  }
}

#[derive(Debug)]
pub struct CGContext(CGContextRef);

impl Default for CGContext {
  fn default() -> Self {
    CGContext(unsafe { UIGraphicsGetCurrentContext() })
  }
}

impl Clone for CGContext {
  fn clone(&self) -> Self {
    CGContext(unsafe { CGContextRetain(self.0) })
  }
}

impl Drop for CGContext {
  fn drop(&mut self) {
    unsafe { CGContextRelease(self.0) };
  }
}

impl CGContext {
  pub fn new() -> CGContext {
    CGContext::default()
  }

  pub fn add_arc(&self, center: CGPoint, radius: CGFloat, start_angle: CGFloat, end_angle: CGFloat, clockwise: bool) {
    unsafe {
      CGContextAddArc(self.0, center.x, center.y, radius, start_angle, end_angle, if clockwise { 1 } else { 0 })
    };
  }

  pub fn move_to_point(&self, point: CGPoint) {
    unsafe { CGContextMoveToPoint(self.0, point.x, point.y) };
  }

  pub fn add_line_to_point(&self, point: CGPoint) {
    unsafe { CGContextAddLineToPoint(self.0, point.x, point.y) };
  }

  pub fn fill_path(&self) {
    unsafe { CGContextFillPath(self.0) };
  }

  pub fn add_quad_curve_to_point(&self, end: CGPoint, control: CGPoint) {
    unsafe { CGContextAddQuadCurveToPoint(self.0, control.x, control.y, end.x, end.y) };
  }

  pub fn stroke_path(&self) {
    unsafe { CGContextStrokePath(self.0) };
  }

  pub fn add_ellipse_in_rect(&self, rect: CGRect) {
    unsafe { CGContextAddEllipseInRect(self.0, rect) };
  }

  pub fn clip(&self) {
    unsafe { CGContextClip(self.0) };
  }
}
